package SubjectTree.Three;

import java.util.Deque;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;

import Utility.TreeNode;

public class BuildTree2 {

/**
 * 难度：中等
 * 
 * 105. 从前序与中序遍历序列构造二叉树
 * 	根据一棵树的前序遍历与中序遍历构造二叉树。
 * 	
 * 注意:
 * 	你可以假设树中没有重复的元素。
 * 	
 * 例如，给出
 * 	前序遍历 preorder = [3,9,20,15,7]
 * 	中序遍历 inorder = [9,3,15,20,7]
 * 	返回如下的二叉树：
 * 	    3
 * 	   / \
 * 	  9  20
 * 	    /  \
 * 	   15   7
 *
 * */
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		BuildTree2 bt = new  BuildTree2();
		System.out.println(bt.buildTree(new int[] {3,9,20,15,7},new int[] {9,3,15,20,7}));
	}
	//自己写(递归)
	public TreeNode buildTree(int[] preorder, int[] inorder) {
		if(preorder.length==0)return null;
		
		int rootValue = preorder[0];
		TreeNode root = new TreeNode(rootValue);
		
		if(preorder.length==1)return root;
		
		int delimiterIndex;
		for(delimiterIndex=0;delimiterIndex<inorder.length;delimiterIndex++) {
			if(inorder[delimiterIndex]==rootValue)break;
		}
		
		int[] leftInorder = new int[delimiterIndex];
		for(int i=0;i<delimiterIndex;i++) {
			leftInorder[i] = inorder[i];
		}
		int[] rightInorder = new int[inorder.length - delimiterIndex - 1];
		for(int i=delimiterIndex+1,j=0;i<inorder.length;i++,j++) {
			rightInorder[j] = inorder[i];
		}
		
		int[] leftPreorder = new int[leftInorder.length];
		for(int i=0;i<leftInorder.length;i++) {
			leftPreorder[i] = preorder[i+1];
		}
		int[] rightPreorder = new int[preorder.length-1-leftInorder.length];
		for(int i=leftInorder.length,j=0;i<preorder.length-1;i++,j++) {
			rightPreorder[j] = preorder[i+1];
		}
		
//		System.out.println("leftInorder :");
//		for (int i : leftInorder) {
//			System.out.print(i+",");
//        }
//		System.out.println("");
//		System.out.println("rightInorder :");
//		for (int i : rightInorder) {
//			System.out.print(i+",");
//        }
//		System.out.println("");
//		System.out.println("leftPreorder :");
//		for (int i : leftPreorder) {
//			System.out.print(i+",");
//        }
//		System.out.println("");
//		System.out.println("rightPreorder :");
//		for (int i : rightPreorder) {
//			System.out.print(i+",");
//        }
//		System.out.println("");
		
		root.left = buildTree(leftPreorder,leftInorder);
		root.right = buildTree(rightPreorder,rightInorder);
		
		return root;
    }
	//方法一：递归
	private Map<Integer, Integer> indexMap;
    public TreeNode myBuildTree(int[] preorder, int[] inorder, int preorder_left, int preorder_right, int inorder_left, int inorder_right) {
        if (preorder_left > preorder_right) {
            return null;
        }

        // 前序遍历中的第一个节点就是根节点
        int preorder_root = preorder_left;
        // 在中序遍历中定位根节点
        int inorder_root = indexMap.get(preorder[preorder_root]);
        
        // 先把根节点建立出来
        TreeNode root = new TreeNode(preorder[preorder_root]);
        // 得到左子树中的节点数目
        int size_left_subtree = inorder_root - inorder_left;
        // 递归地构造左子树，并连接到根节点
        // 先序遍历中「从 左边界+1 开始的 size_left_subtree」个元素就对应了中序遍历中「从 左边界 开始到 根节点定位-1」的元素
        root.left = myBuildTree(preorder, inorder, preorder_left + 1, preorder_left + size_left_subtree, inorder_left, inorder_root - 1);
        // 递归地构造右子树，并连接到根节点
        // 先序遍历中「从 左边界+1+左子树节点数目 开始到 右边界」的元素就对应了中序遍历中「从 根节点定位+1 到 右边界」的元素
        root.right = myBuildTree(preorder, inorder, preorder_left + size_left_subtree + 1, preorder_right, inorder_root + 1, inorder_right);
        return root;
    }
    public TreeNode buildTree1(int[] preorder, int[] inorder) {
        int n = preorder.length;
        // 构造哈希映射，帮助我们快速定位根节点
        indexMap = new HashMap<Integer, Integer>();
        for (int i = 0; i < n; i++) {
            indexMap.put(inorder[i], i);
        }
        return myBuildTree(preorder, inorder, 0, n - 1, 0, n - 1);
    }
    //方法二：迭代
    public TreeNode buildTree2(int[] preorder, int[] inorder) {
        if (preorder == null || preorder.length == 0) {
            return null;
        }
        TreeNode root = new TreeNode(preorder[0]);
        Deque<TreeNode> stack = new LinkedList<TreeNode>();
        stack.push(root);
        int inorderIndex = 0;
        for (int i = 1; i < preorder.length; i++) {
            int preorderVal = preorder[i];
            TreeNode node = stack.peek();
            if (node.val != inorder[inorderIndex]) {
                node.left = new TreeNode(preorderVal);
                stack.push(node.left);
            } else {
                while (!stack.isEmpty() && stack.peek().val == inorder[inorderIndex]) {
                    node = stack.pop();
                    inorderIndex++;
                }
                node.right = new TreeNode(preorderVal);
                stack.push(node.right);
            }
        }
        return root;
    }
}
